/**
 * Lynax' Death System v1.02 - 2005-11-03
 *
 * Inspired by the Aielund-Saga by Stephen Nowland - http://members.optusnet.com.au/brothers2/
 * and by [RPG]Board discussions - http://rpgboard.de/
 * 
 * Features:
 *  - Bleeding from 0 to -9 Hitpoints - Real Death at -10
 *  - Stabilization by Chance - dependent on Constitution and current Hitpoints
 *  - Stabilization by Healing - Regeneration or Spell or other
 *  - Status Saving - regardless wether Player Disconnecting
 *  - Sound Effects - random Groaning
 *  - Visual Effects - Blood Splashes
 *  - Penalty Effects while Disabled - Blind and Deaf
 *  - Penalty Effects after Respawning - Dazed, Slow Healing, Temporary Ability Decreases
 *  - Bonus Effect while Disabled - Etherealness, so Monsters leave them alone
 *  - Singleplayer and Multiplayer
 * 
 * Events to modify: OnPlayerDying, OnPlayerRespawn, OnHeartbeat
 * 
 * Contact:
 * http://www.informatik.uni-oldenburg.de/~hoshi/rpg/nwn/
 * 
 * History:
 * 1.02 - 2005-11-03
 *  - added missing declarations
 *  - fixed wrong variable names
 * 1.01 - 2005-10-31
 *  - slowed down regeneration time on respawn
 *  - removed CON-drain on respawn (unfinished code that got there by mistake)
 *  - removed unneccessary comments
 *  - improved readability
 * 1.00 - 2005-10-18
 *  - first public release
 */

/********************* add to OnHeartbeat: ***********************/

#include "bleeding_config"
#include "nw_i0_spells"

void doBleeding( object oPC, int HP ){
    ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_COM_BLOOD_REG_RED ), oPC );
    ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectDamage( 1, DAMAGE_TYPE_DIVINE, DAMAGE_POWER_ENERGY ), oPC );
    SetLocalInt( oPC, "LastHitPoints", HP-1 );
    if( HP <= -9 ){
        ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_COM_BLOOD_LRG_RED ), oPC );
        ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectDeath( FALSE, FALSE ), oPC );
//    }else switch( HP<7 ? d2() : ( HP<3 ? d4() : d6() ) ){  // doesn't seem to work, further testing neccessary
    }else switch( d6() ){
        case 1: PlayVoiceChat( VOICE_CHAT_NEARDEATH, oPC ); break;
        case 2: PlayVoiceChat( VOICE_CHAT_PAIN1,     oPC ); break;
        case 3: PlayVoiceChat( VOICE_CHAT_PAIN2,     oPC ); break;
        case 4: PlayVoiceChat( VOICE_CHAT_PAIN3,     oPC ); break;
        case 5: PlayVoiceChat( VOICE_CHAT_HEALME,    oPC ); break;
        case 6: PlayVoiceChat( VOICE_CHAT_HELP,      oPC ); break;
    }
}

void stabilizationCheck( object oPC, int HP ){
    int roll = d100();
    int chance = GetAbilityModifier( ABILITY_CONSTITUTION, oPC )*5+25 + 5*HP+25;
    if( roll <= chance ){
        SendMessageToPC( oPC, "Stabilization Check : *Success* (" + IntToString(roll) + " <= " + IntToString(chance) + ")" );
        SetLocalInt( oPC, "PlayerHealth", PC_HEALTH_STABLE );
    }else{
        SendMessageToPC( oPC, "Stabilization Check : *Failure* (" + IntToString(roll) + " > "  + IntToString(chance) + ")" );
        doBleeding( oPC, HP );
    }
}

void removeDisabledPenalties( object oPC ){
    RemoveSpecificEffect( EFFECT_TYPE_BLINDNESS, oPC );
    RemoveSpecificEffect( EFFECT_TYPE_DEAF,      oPC );
    RemoveSpecificEffect( EFFECT_TYPE_ETHEREAL,  oPC );
}

void applyRisePenalties( object oPC ){
    RemoveSpecificEffect( IMMUNITY_TYPE_MIND_SPELLS, oPC );
    ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectDazed(), oPC, 18.0f );
    int i;
    for( i=1; i<6; i++ ){
        ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectAbilityDecrease( ABILITY_STRENGTH,  1 ), oPC, i*18.0f );
        ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectAbilityDecrease( ABILITY_DEXTERITY, 1 ), oPC, i*18.0f );
    }
}

int isDying( object oPC ){
    int iPlayerHealth = GetLocalInt( oPC, "PlayerHealth" );
    if( iPlayerHealth == PC_HEALTH_ALIVE || iPlayerHealth == PC_HEALTH_DEAD ){
        return FALSE;           // alive, or at least not dying
    }
    int HP = GetCurrentHitPoints( oPC );
    if( HP > 0 ){               // must've been healed or something
        removeDisabledPenalties( oPC );
        SetLocalInt( oPC, "PlayerHealth", PC_HEALTH_ALIVE );
        return FALSE;           // alive
    }
    else if( HP < -9 ){         // must've been taken damage or something
        SetLocalInt( oPC, "PlayerHealth", PC_HEALTH_DEAD );
        return FALSE;           // dead
    }
    if( HP > GetLocalInt( oPC, "LastHitPoints" ) ){  // has been healed lately?
        SetLocalInt( oPC, "PlayerHealth", PC_HEALTH_STABLE );
        iPlayerHealth = PC_HEALTH_STABLE;
    }
    if( iPlayerHealth == PC_HEALTH_DYING ){
        stabilizationCheck( oPC, HP );
        return TRUE;
    }
    ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectHeal( 1 ), oPC );
    SetLocalInt( oPC, "LastHitPoints", HP+1 );
    if( HP == 0 ){
        removeDisabledPenalties( oPC );
        applyRisePenalties( oPC );
    }
    return TRUE;
}

int checkPlayerHealthStatus(){
    int bEverybodyReallyAlive = TRUE;
    object oPC = GetFirstPC();
    while( GetIsObjectValid( oPC ) ){
        if( isDying( oPC ) )
            bEverybodyReallyAlive = FALSE;
        oPC = GetNextPC();
    }
    return bEverybodyReallyAlive;
}

void main() {
    object oModule = GetModule();
    if( !GetLocalInt( oModule, "EverybodyAlive" ) )
         SetLocalInt( oModule, "EverybodyAlive", checkPlayerHealthStatus() );
}


/********************* add to OnPlayerDying: ***********************/

#include "bleeding_config"

void main() {
    object oPC = GetLastPlayerDying();
    object oModule = GetModule();
    SetLocalInt( oModule, "EverybodyAlive", FALSE );
    SetLocalInt( oPC, "PlayerHealth", PC_HEALTH_DYING );
    SetLocalInt( oPC, "LastHitPoints", GetCurrentHitPoints( oPC ) );
    AssignCommand( oPC, ClearAllActions() );
    ApplyEffectToObject( DURATION_TYPE_PERMANENT, EffectBlindness(), oPC );
    ApplyEffectToObject( DURATION_TYPE_PERMANENT, EffectDeaf(),      oPC );
    ApplyEffectToObject( DURATION_TYPE_PERMANENT, EffectEthereal(),  oPC );  // enemies stop attack
}


/********************* add to OnPlayerRespawn: ***********************/

...
    RemoveEffects( oRespawner );  // removes all negative effects

    // ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectHeal( GetMaxHitPoints( oRespawner ) ), oRespawner );                        // instant heal
    ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectRegenerate( 1, 12.0 ), oRespawner, GetMaxHitPoints( oRespawner ) * 12.0 );  // slow heal

    // damit beim folgenden KO-Entzug keiner unter 0 kommt
    // ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectTemporaryHitpoints( GetHitDice( oRespawner ) ), oRespawner, 18.5f );  // experimental

    int i;
    for( i=1; i<11; i++ ){    // unter 3 kann sowieso nix gesetzt werden
        // DelayCommand( i*18-0.5, ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectTemporaryHitpoints( GetHitDice( oRespawner ) ), oRespawner, 19.0f ) );  // experimental
        ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectAbilityDecrease( ABILITY_STRENGTH,     1 ), oRespawner, i*18.0f );
        ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectAbilityDecrease( ABILITY_DEXTERITY,    1 ), oRespawner, i*18.0f );
        // ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectAbilityDecrease( ABILITY_CONSTITUTION, 1), oRespawner, i*18.0f );  // experimental
    }
    // ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectDeaf(), oRespawner, 6.0f );
    // ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectBlindness(), oRespawner, 12.0f );
    // ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectKnockdown(), oRespawner, 18.0f );
    // ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectSlow(), oRespawner, 30.0f ); // -> automatisch durch ST-Malus
    ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectDazed(), oRespawner, 30.0f );
...


/********************* add to resources: ***********************/

"bleeding_config"

// *** Constants - DO NOT CHANGE THESE! ***
const int PC_HEALTH_ALIVE   = 0;    // HP > 0
const int PC_HEALTH_DYING   = 1;    // -10 < HP <= 0 & Bleeding
const int PC_HEALTH_STABLE  = 2;    // -10 < HP <= 0 & Stabilized
const int PC_HEALTH_DEAD    = 3;    // HP <= -10
